package org.juggers.server;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;

import org.juggers.domain.InvalidCaptchaException;
import org.juggers.domain.JugEvent;
import org.juggers.domain.Jugger;
import org.juggers.remote.JuggersService;

import com.google.gdata.client.calendar.CalendarQuery;
import com.google.gdata.client.calendar.CalendarService;
import com.google.gdata.data.calendar.CalendarEventEntry;
import com.google.gdata.data.calendar.CalendarEventFeed;
import com.google.gdata.data.calendar.EventWho;
import com.google.gdata.data.extensions.Who.Rel;
import com.google.gdata.model.gd.AttendeeStatus;
import com.google.gdata.model.gd.AttendeeType;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * Default juggers calendar service implementation
 * 
 * @author michel@breizhjug.org
 */
@SuppressWarnings("serial")
public class JuggersServiceServlet
    extends RemoteServiceServlet
    implements JuggersService
{
    private static final String SEP = ";";

    /** Private session Id */
    private static final String SESSION_TOKEN = Configuration.getInstance().getgCalendarSession();

    /** Private calendar URL */
    private static final String CALENDAR_URL =
        "http://www.google.com/calendar/feeds/admin@juggers.org/private/full";

    /** GData calendar service */
    private CalendarService calendarService = new CalendarService( "juggers.org" );
    
    /** ReCaptcha check */
    private CaptchaCheck captchaCheck = new CaptchaCheck();

    private MailingService mailingService = new MailingService();

    @Override
    public void init( ServletConfig config )
        throws ServletException
    {
        super.init( config );
        System.out.println( "init" );
        calendarService.setAuthSubToken( SESSION_TOKEN, null );
    }

    @Override
    public List<JugEvent> getEvents()
    {
        CalendarEventFeed calendar = getCalendar();

        List<JugEvent> events = new ArrayList<JugEvent>();
        for ( CalendarEventEntry entry : calendar.getEntries() )
        {
            events.add( convert( entry ) );
        }

        return events;
    }

    @Override
    public List<Jugger> getParticipants( String eventId )
    {
        CalendarEventEntry entry = getEventEntry( eventId );
        List<Jugger> participants = getParticipants( entry );
        Collections.shuffle( participants );
        return participants;
    }

    /**
     * Get the CalendarEventEntry participants list
     * 
     * @param entry CalendarEventEntry
     * @return List of participants
     */
    private List<Jugger> getParticipants( CalendarEventEntry entry )
    {
        List<Jugger> participants = new ArrayList<Jugger>();
        for ( EventWho participant : entry.getParticipants() )
        {
            if ( participant.getRel().equals( Rel.EVENT_ATTENDEE ) )
            {
                participants.add( convert( participant ) );
            }
        }
        return participants;
    }

    @Override
    public void subscribe( String id, String captchaChallenge, String captchaResponse, Jugger jugger ) throws InvalidCaptchaException
    {
        String remoteAddr = getThreadLocalRequest().getRemoteAddr();
        if ( !captchaCheck.isValid( captchaChallenge, captchaResponse, remoteAddr ) )
        {
    		throw new InvalidCaptchaException();
    	}
    	
        CalendarEventEntry entry = getEventEntry( id );
        if ( entry != null )
        {
            JugEvent event = convert( entry );
            EventWho who = convert( jugger );
            String confirmationToken = UUID.randomUUID().toString();
            jugger.setConfirmationToken( confirmationToken );
            entry.addParticipant( who );
            updateEntry( entry );
            
            mailingService.sendConfirmationMail( event, jugger );
        }
        else
        {
            // oups !
        }
    }


    /**
     * Get a CalendarEventEntry
     * 
     * @param id EntryId
     * @return CalendarEventEntry
     */
    private CalendarEventEntry getEventEntry( String id )
    {
        // TODO Maybe find a better way to do this
        CalendarEventFeed calendar = getCalendar();
        for ( CalendarEventEntry entry : calendar.getEntries() )
        {
            if ( entry.getId().equals( id ) )
            {
                return entry;
            }
        }
        return null;
    }

    /**
     * Update an Entry
     * 
     * @param entry Entry
     */
    private void updateEntry( CalendarEventEntry entry )
    {
        try
        {
            entry.update();
        }
        catch ( Exception ex )
        {
            // logger.error("Error updating event : " + ex.getMessage());
        }
    }

    /**
     * Get upcoming events calendar
     * 
     * @return CalendarEventFeed
     */
    private CalendarEventFeed getCalendar()
    {
        try
        {
            CalendarQuery query = new CalendarQuery( new URL( CALENDAR_URL ) );
    
            // Only upcoming events order by date
            query.setStringCustomParameter( "orderby", "starttime" );
            query.setStringCustomParameter( "futureevents", "true" );
            
            return calendarService.query( query, CalendarEventFeed.class );
        }
        catch ( Exception ex )
        {
            // logger.error("Error retrieving calendar events : " + ex.getMessage());
        }
        return null;
    }

    /**
     * Convert a CalendarEventEntry to an Event
     * 
     * @param entry CalendarEventEntry
     * @return Event
     */
    private JugEvent convert( CalendarEventEntry entry )
    {
        JugEvent event = new JugEvent( entry.getId() );
        event.setTitle( entry.getTitle().getPlainText() );
        event.setSummary( entry.getTextContent().getContent().getPlainText() );
        event.setLocation( entry.getLocations().get( 0 ).getValueString() );
        event.setDateTime( new Date( entry.getTimes().get( 0 ).getStartTime().getValue() ) );
        event.setParticipants( getParticipants( entry ).size() );
        return event;
    }

    private Jugger convert( EventWho participant )
    {
        String fullName = participant.getValueString();
        String[] split = fullName.split( SEP );
        String firstName = split[0];
        String lastName = split.length > 1 ? split[1] : "";
        Jugger jugger = new Jugger( firstName, lastName, participant.getEmail() );
        if ( split.length > 2 )
        {
            jugger.setConfirmationToken( split[2] );
        }
        return jugger;
    }

    /**
     * Create an attendee
     * 
     * @param firstName First name
     * @param lastName Last name
     * @param eMail e-Mail
     * @return Attendee
     */
    private EventWho convert( Jugger jugger )
    {
        EventWho who = new EventWho();
        who.setEmail( jugger.geteMail() );
        who.setValueString( jugger.getFirstName() + SEP + jugger.getLastName() + SEP + jugger.getConfirmationToken() );
        who.setAttendeeType( AttendeeType.Value.OPTIONAL );
        who.setAttendeeStatus( AttendeeStatus.Value.ACCEPTED );
        who.setRel( Rel.EVENT_ATTENDEE );
        return who;
    }
}
